﻿using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;

namespace DirectX_Tutorial
{
    struct myownvertexformat
    {
        public Vector3 Pos;
        public Vector2 TexCoord;

        public myownvertexformat(Vector3 _Pos, float texx, float texy)
        {
            Pos = _Pos;
            TexCoord.X = texx;
            TexCoord.Y = texy;
        }
    }

    public class WinForm : System.Windows.Forms.Form
    {
        private System.ComponentModel.Container components = null;
        private D3D.Device device;
        private VertexBuffer vb;
        private Vector3 CameraPos;
        private VertexDeclaration vd;
        private Effect effect;
        private float brightness = 1.0f;

        private Texture LenaTexture;

        private Matrix matView;
        private Matrix matProjection;

        private int LastTickCount = 1;
        private int Frames = 0;
        private float LastFrameRate = 0;
        private D3D.Font text;

        public WinForm()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        public void InitializeDevice()
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;

            Caps DevCaps = D3D.Manager.GetDeviceCaps(0, D3D.DeviceType.Hardware);
            D3D.DeviceType DevType = D3D.DeviceType.Reference;
            CreateFlags DevFlags = CreateFlags.SoftwareVertexProcessing;
            if ((DevCaps.VertexShaderVersion >= new Version(2, 0)) && (DevCaps.PixelShaderVersion >= new Version(2, 0)))
            {
                DevType = D3D.DeviceType.Hardware;
                if (DevCaps.DeviceCaps.SupportsHardwareTransformAndLight)
                {
                    DevFlags = CreateFlags.HardwareVertexProcessing;
                    if (DevCaps.DeviceCaps.SupportsPureDevice)
                    {
                        DevFlags |= CreateFlags.PureDevice;
                    }
                }
            }

            device = new D3D.Device(0, DevType, this, DevFlags, presentParams);
            device.DeviceReset += new EventHandler(this.HandleDeviceReset);
        }

        private void HandleDeviceReset(object sender, EventArgs e)
        {
            FillResources();
        }

        private void AllocateResources()
        {
            vb = new VertexBuffer(typeof(myownvertexformat), 6, device, Usage.WriteOnly, VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture0, Pool.Managed);
            InitializeFont();
            effect = D3D.Effect.FromFile(device, "c:\\x\\simple\\L3a.fx", null, ShaderFlags.None, null);
        }

        private void FillResources()
        {
            myownvertexformat[] vertices = new myownvertexformat[6];
            vertices[0] = new myownvertexformat(new Vector3(-2f, 2f, 0f), 1f, 0f);
            vertices[1] = new myownvertexformat(new Vector3(-2f, -2f, 0f), 1f, 1f);
            vertices[2] = new myownvertexformat(new Vector3(2f, -2f, 0f), 0f, 1f);
            vertices[3] = new myownvertexformat(new Vector3(2f, -2f, 0f), 0f, 1f);
            vertices[4] = new myownvertexformat(new Vector3(2f, 2f, 0f), 0f, 0f);
            vertices[5] = new myownvertexformat(new Vector3(-2f, 2f, 0f), 1f, 0f);

            vb.SetData(vertices, 0, LockFlags.None);

            SetUpCamera();

            VertexElement[] velements = new VertexElement[]
             {
                 new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                 new VertexElement(0, 12, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
                 VertexElement.VertexDeclarationEnd
             };
            vd = new VertexDeclaration(device, velements);

            LenaTexture = TextureLoader.FromFile(device, "wroclaw.jpg");
            //LenaTexture = TextureLoader.FromFile(device, "lena.png");
        }

        private void InitializeFont()
        {
            System.Drawing.Font systemfont = new System.Drawing.Font("Arial", 12f, FontStyle.Regular);
            text = new D3D.Font(device, systemfont);
        }

        private void DrawMesh(Mesh mesh, Material[] meshmaterials, Texture[] meshtextures)
        {
            for (int i = 0; i < meshmaterials.Length; i++)
            {
                if (meshtextures.Length > 3) device.SetTexture(0, meshtextures[i]);
                mesh.DrawSubset(i);
            }
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
            device.BeginScene();

            device.SetStreamSource(0, vb, 0);
            device.VertexDeclaration = vd;
            effect.Technique = "Simplest";
            effect.SetValue("xViewProjection", matView * matProjection);
            effect.SetValue("xColoredTexture", LenaTexture);

            int numpasses = effect.Begin(0);
            for (int i = 0; i < numpasses; i++)
            {
                effect.BeginPass(i);
                device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);
                effect.EndPass();
            }
            effect.End();

            UpdateFramerate();

            device.EndScene();
            device.Present();
            this.Invalidate();
        }

        private void UpdateFramerate()
        {
            Frames++;
            if (Math.Abs(Environment.TickCount - LastTickCount) > 1000)
            {
                LastFrameRate = (float)Frames * 1000 / Math.Abs(Environment.TickCount - LastTickCount);
                LastTickCount = Environment.TickCount;
                Frames = 0;
            }
            text.DrawText(null, string.Format("Framerate : {0:0.00} fps", LastFrameRate), new Point(10, 430), Color.Red);
        }

        private void SetUpCamera()
        {
            CameraPos = new Vector3(0, -6, 5);
            matProjection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 0.3f, 50f);
            matView = Matrix.LookAtLH(new Vector3(0, 0, 6), new Vector3(0, 0, 0), new Vector3(0, 1, 0));

            device.Transform.Projection = matProjection;
            device.Transform.View = matView;
        }

        private void LoadMesh(string filename, ref Mesh mesh, ref Material[] meshmaterials, ref Texture[] meshtextures)
        {
            ExtendedMaterial[] materialarray;
            GraphicsStream adj = null;

            mesh = Mesh.FromFile(filename, MeshFlags.Managed, device, out adj, out materialarray);

            if ((materialarray != null) && (materialarray.Length > 0))
            {
                meshmaterials = new Material[materialarray.Length];
                meshtextures = new Texture[materialarray.Length];

                for (int i = 0; i < materialarray.Length; i++)
                {
                    meshmaterials[i] = materialarray[i].Material3D;
                    meshmaterials[i].Ambient = meshmaterials[i].Diffuse;

                    if ((materialarray[i].TextureFilename != null) && (materialarray[i].TextureFilename != string.Empty))
                    {
                        meshtextures[i] = TextureLoader.FromFile(device, materialarray[i].TextureFilename);
                    }
                }
            }

            mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalTextured.Format, device);
            mesh.ComputeNormals();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.Size = new System.Drawing.Size(500, 500);
            this.Text = "Zadanie 3";
        }

        static void Main()
        {
            using (WinForm our_directx_form = new WinForm())
            {
                our_directx_form.InitializeDevice();
                our_directx_form.AllocateResources();
                our_directx_form.FillResources();
                Application.Run(our_directx_form);
            }
        }
    }
}